

import java.io.File;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.lazy.IB1;
import weka.classifiers.lazy.IBk;
import weka.classifiers.trees.*;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSink;
import weka.core.converters.ConverterUtils.DataSource;


public class WekaWorker {

	private String dataset;
	private File datasetHandle;
	private Instances dataWeka;
	
	// classifiers
	private Classifier tree;
	
	public WekaWorker () {
	}
	
	public void loadInstances (Instances instances) {
		this.dataWeka = instances;
	}
	
	/**
	 * Loads a dataset from an ARFF file into the object
	 * @param filename - the absolute path of the ARFF object
	 * @return true of false depending on whether the load was successful
	 */
	public boolean loadFile (String filename) {
		this.dataset = filename;
		
		try {
			this.datasetHandle = new File ("filename");
			this.dataWeka = DataSource.read (filename);
                        this.dataWeka.setClassIndex (this.dataWeka.numAttributes() - 1);
                        System.out.println (dataWeka.numAttributes() - 1);
		}
		catch (NullPointerException e) {
			e.printStackTrace();
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		
		return true;
	}
	
	/** 
	 * Sets up a J48 tree classifier to be used with the current data. Data has to be loaded into the object first!
	 * @return true or false depending on whether the set up was successful
	 */
	public boolean setupClassifierJ48 () {
		String[] options = new String[1];
		options[0] = "-U";
		this.tree = new J48 ();
		
		try {
			this.tree.setOptions (options);
			this.tree.buildClassifier (this.dataWeka);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		
		return true;
	}

        /**
	 * Sets up a Id3 tree classifier to be used with the current data. Data has to be loaded into the object first!
	 * @return true or false depending on whether the set up was successful
	 */
	public boolean setupClassifierId3 () {
		String[] options = new String[1];
		options[0] = "-U";
		this.tree = new Id3 ();

		try {
			this.tree.setOptions (options);
			this.tree.buildClassifier (this.dataWeka);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

        /**
	 * Sets up a BF tree classifier to be used with the current data. Data has to be loaded into the object first!
	 * @return true or false depending on whether the set up was successful
	 */
	public boolean setupClassifierBF () {
		String[] options = new String[1];
		options[0] = "-U";
		this.tree = new BFTree();

		try {
			this.tree.setOptions (options);
			this.tree.buildClassifier (this.dataWeka);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

        /**
	 * Sets up a IB1 classifier to be used with the current data. Data has to be loaded into the object first!
	 * @return true or false depending on whether the set up was successful
	 */
	public boolean setupClassifierIB1 () {
		String[] options = new String[1];
		//options[0] = "-U";
		this.tree = new IB1();

		try {
			//this.tree.setOptions (options);
			this.tree.buildClassifier (this.dataWeka);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

        /**
	 * Sets up a IB5classifier to be used with the current data. Data has to be loaded into the object first!
	 * @return true or false depending on whether the set up was successful
	 */
	public boolean setupClassifierIB5 () {
		String[] options = new String[1];
		//options[0] = "-U";
		this.tree = new IBk(5);

		try {
			//this.tree.setOptions (options);
			this.tree.buildClassifier (this.dataWeka);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

	/**
	 * Evaluates a test set and returns a WekaResult object with all of the
	 * data, mentioned in the manual.
	 * @param testset - the filename of the test set to be used
	 * @return A WekaResult object with evaluation data
	 */
	public WekaResult getEvaluationJ48 (String testset) {
		try {
			Instances testSetWeka = DataSource.read (testset);
                        testSetWeka.setClassIndex (testSetWeka.numAttributes() - 1);
			Evaluation eval = new Evaluation (this.dataWeka);
			eval.evaluateModel (tree, testSetWeka);
			System.out.println (eval.toSummaryString());
			return new WekaResult (eval);
			
		} catch (Exception e) {
			
			e.printStackTrace();
			return null;
		}
	}


	
	/**
	 * This method classifies every instance in an Instances object and returns
	 * an Instances object, containing the classified instances.
	 * @param testSet - the filename of the test set to use
	 * @return An Instances object, containing the classified instances
	 */
	public Instances getClassification (String testSet) {
		
		try {
			Instances unlabeled;
			unlabeled = DataSource.read (testSet);
			unlabeled.setClassIndex (unlabeled.numAttributes() - 1);
			Instances labeled = new Instances (unlabeled);
			
			// now we classify
			for (int i = 0; i < unlabeled.numInstances(); i++) {
				double clsLabel = tree.classifyInstance(unlabeled.instance(i));		// I have no idea why this is a double
				labeled.instance(i).setClassValue(clsLabel);
			}

			return labeled;
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * This method writes the loaded dataset to disk.  
	 * @param filename - the filename of the file to be written
	 * @return true or false depending on whether the write was successful
	 */
	public boolean writeToDisk (String filename) {
		try {
			DataSink.write (filename, this.dataWeka);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public void unloadSet () {
		this.dataset = null;
		this.datasetHandle = null;
		this.dataWeka = null;
		this.tree = null;
	}
}
